/***********************************
	???????????????HAL??��
***********************************/
#include <My_Inc/PHY/PHY_oled.h>	  //????
#include <My_Inc/PHY/PHY_oled_font.h> //??????

#define LEFT 0x27
#define RIGHT 0x26
#define UP 0X29
#define DOWM 0x2A
#define ON 0xA7
#define OFF 0xA6

uint16_t const displayWidth = 128;
uint16_t const displayHeight = 64;

/*  OLED???
[0]0 1 2 3 ... 127
[1]0 1 2 3 ... 127
[2]0 1 2 3 ... 127
[3]0 1 2 3 ... 127
[4]0 1 2 3 ... 127
[5]0 1 2 3 ... 127
[6]0 1 2 3 ... 127
[7]0 1 2 3 ... 127 */
static uint8_t OLED_RAM[8][128];

void LL_I2C_WriteByte(uint8_t addr, uint8_t data)
{
	//LL库模拟IIC
	OLED_I2C_Start_LL();
	OLED_I2C_Send_Byte_LL(0x78);  // 发送器件地址+写标志(0x78是原函数中的地址)
	OLED_I2C_Wait_Ack_LL();
	OLED_I2C_Send_Byte_LL(addr);  // 发送寄存器地址
	OLED_I2C_Wait_Ack_LL();
	OLED_I2C_Send_Byte_LL(data);  // 发送数据
	OLED_I2C_Wait_Ack_LL();
	OLED_I2C_Stop_LL();
}

void WriteCmd(uint8_t IIC_Command)
{
	LL_I2C_WriteByte(0x00, IIC_Command);
}

void WriteDat(uint8_t IIC_Data)
{
	LL_I2C_WriteByte(0x40, IIC_Data);
}

void OLED_Init(void)
{
	HAL_Delay(100);

	WriteCmd(0xAE); //?????
	WriteCmd(0x20); //???????????

	WriteCmd(0x10); // 00????????;01?????????;10?????????(????);11????��
	WriteCmd(0xb0); //??????????????�D??????0-7
	WriteCmd(0x00); //---??????��??
	WriteCmd(0x10); //---??????��??

	WriteCmd(0xc8); //????COM?????��??
	WriteCmd(0x40); //--????????��??
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //??????? 0x00~0xff
	WriteCmd(0xa1); //--????????????0??127
	WriteCmd(0xa6); //--???????????
	WriteCmd(0xa8); //--????????(1 ~ 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); // 0xa4,??????RAM????;0xa5,Output????RAM????
	WriteCmd(0xd3); //-???????????
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--????????????/???????
	WriteCmd(0xf0); //--???��???
	WriteCmd(0xd9); //--????pre-charge???
	WriteCmd(0x22); //
	WriteCmd(0xda); //--????com????????????
	WriteCmd(0x12);
	WriteCmd(0xdb); //--????vcomh
	WriteCmd(0x20); // 0x20,0.77xVcc
	WriteCmd(0x8d); //--????DC-DC
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--??oled???

	OLED_FullyClear(); //????
}

/**************************************************************
	 Prototype      : void OLED_ON(void)
	 Parameters     : none
	 return					: none
	 Description    : ??OLED???????��???
***************************************************************/
void OLED_ON(void)
{
	WriteCmd(0X8D); //???????
	WriteCmd(0X14); //????????
	WriteCmd(0XAF); // OLED????
}

/**************************************************************
	 Prototype      : void OLED_OFF(void)
	 Parameters     : none
	 return					: none
	 Description    : ??OLED???? -- ????????,OLED???????10uA
***************************************************************/
void OLED_OFF(void)
{
	WriteCmd(0X8D); //???????
	WriteCmd(0X10); //??????
	WriteCmd(0XAE); // OLED????
}

/**************************************************************
	 Prototype      : void OLED_RefreshRAM(void)
	 Parameters     : none
	 return					: none
	 Description    : ??????
***************************************************************/
void OLED_RefreshRAM(void)
{
	// ????????
	for (uint16_t m = 0; m < displayHeight / 8; m++)
	{
		WriteCmd(0xb0 + m); //????????b0~b7
		WriteCmd(0x00);		//???????��?��??��???00-0f
		WriteCmd(0x10);		//???????��?��??��???10-1f
		for (uint16_t n = 0; n < displayWidth; n++)
		{
			WriteDat(OLED_RAM[m][n]);
		}
	}
}

/**************************************************************
	 Prototype      : void OLED_ClearRAM(void)
	 Parameters     : none
	 return					: none
	 Description    : ????????????
***************************************************************/
void OLED_ClearRAM(void)
{
	for (uint16_t m = 0; m < displayHeight / 8; m++)
	{
		for (uint16_t n = 0; n < displayWidth; n++)
		{
			OLED_RAM[m][n] = 0x00;
		}
	}
}

/**************************************************************
	 Prototype      : void OLED_Fill(uint8_t fill_Data)
	 Parameters     : fill_Data ????1???????
	 return					: none
	 Description    : ?????? 0x00~0xff
***************************************************************/
void OLED_FullyFill(uint8_t fill_Data)
{
	for (uint16_t m = 0; m < displayHeight / 8; m++)
	{
		for (uint16_t n = 0; n < displayWidth; n++)
		{
			OLED_RAM[m][n] = fill_Data;
		}
	}

	OLED_RefreshRAM();
}

/**************************************************************
	 Prototype      : void OLED_FullyClear(void)
	 Parameters     : none
	 return					: none
	 Description    : ??????
***************************************************************/
void OLED_FullyClear(void)
{
	OLED_FullyFill(RESET_PIXEL);
}

/**************************************************************
	Prototype      :  void OLED_SetPixel(int16_t x, int16_t y, uint8_t set_pixel)
	Parameters     : 	x,y -- ?????????(x:0~127, y:0~63);
										set_pixel	 ????????  SET_PIXEL = 1, RESET_PIXEL = 0
	return				 :  none
	Description    : 	?????????????????
***************************************************************/
void OLED_SetPixel(int16_t x, int16_t y, uint8_t set_pixel)
{
	if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight)
	{
		if (set_pixel)
		{
			OLED_RAM[y / 8][x] |= (0x01 << (y % 8));
		}
		else
		{
			OLED_RAM[y / 8][x] &= ~(0x01 << (y % 8));
		}
	}
}

/**************************************************************
	Prototype      :  void OLED_GetPixel(int16_t x, int16_t y)
	Parameters     : 	x,y -- ?????????(x:0~127, y:0~63);
	return				 :  PixelStatus ??????? 	SET_PIXEL = 1, RESET_PIXEL = 0
	Description    : 	????????????????
***************************************************************/
PixelStatus OLED_GetPixel(int16_t x, int16_t y)
{
	if (OLED_RAM[y / 8][x] >> (y % 8) & 0x01)
		return SET_PIXEL;

	return RESET_PIXEL;
}

/**************************************************************
	Prototype      : void OLED_ShowStr(int16_t x, int16_t y, uint8_t ch[], uint8_t TextSize)
	Parameters     : 	x,y -- ?????????(x:0~127, y:0~63);
										ch[] -- ???????????;
										TextSize -- ?????��(1:6*8 ; 2:8*16)
	return				 :  none
	Description    : 	???codetab.h?��?ASCII???,??6*8??8*16?????
***************************************************************/
void OLED_ShowStr(int16_t x, int16_t y, uint8_t ch[], uint8_t TextSize)
{
	if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight)
	{
		int32_t c = 0;
		uint8_t j = 0;

		switch (TextSize)
		{
		case 1:
		{
			while (ch[j] != '\0')
			{
				c = ch[j] - 32;
				if (c < 0) //??��???
					break;

				if (x >= 125 || (127 - x < 6)) //????????????????21??????,??????��?????? || ?????��??6???????????????????????
				{
					x = 0;
					y += 8;			//???????
					if (63 - y < 8) // ??????????????????
						break;
				}
				for (uint8_t m = 0; m < 6; m++)
				{
					for (uint8_t n = 0; n < 8; n++)
					{
						OLED_SetPixel(x + m, y + n, (F6x8[c][m] >> n) & 0x01);
					}
				}
				x += 6;
				j++;
			}
		}
		break;
		case 2:
		{
			while (ch[j] != '\0')
			{
				c = ch[j] - 32;
				if (c < 0) //??��???
					break;

				if (x >= 127 || (127 - x < 8)) // 16?????? || ?????��??8???????????????????????
				{
					x = 0;
					y += 16;		 //???????
					if (63 - y < 16) // ??????????????????
						break;
				}
				for (uint8_t m = 0; m < 2; m++)
				{
					for (uint8_t n = 0; n < 8; n++)
					{
						for (uint8_t i = 0; i < 8; i++)
						{
							OLED_SetPixel(x + n, y + i + m * 8, (F8X16[c][n + m * 8] >> i) & 0x01);
						}
					}
				}
				x += 8;
				j++;
			}
		}
		break;
		}
	}
	OLED_RefreshRAM();
}

/**************************************************************
	 Prototype      : void OLED_ShowCN(int16_t x, int16_t y, uint8_t* ch)
	 Parameters     : x,y -- ?????????(x:0~127, y:0~7);
										CN[]:??????codetab.h?��?????
	 return				  : none
	 Description    : ???codetab.h?��????,16*16????
***************************************************************/
void OLED_ShowCN(int16_t x, int16_t y, uint8_t *ch)
{
	if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight)
	{
		int32_t len = 0, offset = sizeof(F16x16_CN[0].index);

		while (ch[len] != '\0')
		{
			if (x >= 127 || (127 - x < 16)) // 8?????????||?????��??16???????????????????????
			{
				x = 0;
				y += 16;
				if (63 - y < 16) // ??????????????????
					break;
			}

			//??????????????????????????????
			for (uint8_t i = 0; i < sizeof(F16x16_CN) / sizeof(GB2312_CN); i++)
			{
				if (((F16x16_CN[i].index[0] == ch[len]) && (F16x16_CN[i].index[1] == ch[len + 1])))
				{
					for (uint8_t m = 0; m < 2; m++) //?
					{
						for (uint8_t n = 0; n < 16; n++) // ??
						{
							for (uint8_t j = 0; j < 8; j++) // ??
							{
								OLED_SetPixel(x + n, y + j + m * 8, (F16x16_CN[i].encoder[n + m * 16] >> j) & 0x01);
							}
						}
					}
					x += 16;
					len += offset;
					break;
				}
				else if (F16x16_CN[i].index[0] == ch[len] && ch[len] == 0x20)
				{
					for (uint8_t m = 0; m < 2; m++)
					{
						for (uint8_t n = 0; n < 16; n++)
						{
							for (uint8_t j = 0; j < 8; j++)
							{
								OLED_SetPixel(x + n, y + j + m * 8, (F16x16_CN[i].encoder[n + m * 16] >> j) & 0x01);
							}
						}
					}
					x += 16;
					len++;
					break;
				}
			}
		}
	}
	OLED_RefreshRAM();
}

/**************************************************************
	 Prototype      : void OLED_Show_MixedCH(int16_t x, int16_t y, uint8_t* ch)
	 Parameters     : x,y -- ?????????(x:0~127, y:0~7); CN[]:??????codetab.h?��?????
	 return				  : none
	 Description    : ???codetab.h?��????,16*16????,???,8*16????
***************************************************************/
void OLED_ShowMixedCH(int16_t x, int16_t y, uint8_t *ch)
{
	if (x >= 0 && x < displayWidth && y >= 0 && y < displayHeight)
	{
		int32_t len = 0, c, offset = sizeof(F16x16_CN[0].index);

		while (ch[len] != '\0')
		{
			if (ch[len] >= 0xa1) // GB2312??0xA1A0???
			{
				for (uint8_t i = 0; i < sizeof(F16x16_CN) / sizeof(GB2312_CN); i++)
				{
					if (((F16x16_CN[i].index[0] == ch[len]) && (F16x16_CN[i].index[1] == ch[len + 1])))
					{
						if (x >= 127 || (127 - x < 16)) // 8?????????||?????��??16???????????????????????
						{
							x = 0;
							y += 16;
							if (63 - y < 16) // ??????????????????
								break;
						}
						for (uint8_t m = 0; m < 2; m++) //?
						{
							for (uint8_t n = 0; n < 16; n++) //??
							{
								for (uint8_t j = 0; j < 8; j++) //??
								{

									OLED_SetPixel(x + n, y + j + m * 8, (F16x16_CN[i].encoder[n + m * 16] >> j) & 0x01);
								}
							}
						}
						x += 16;
						len += offset;
						break;
					}
				}
			}
			else if (ch[len] <= 127) // ASCII????��0-127
			{
				c = ch[len] - 32;
				if (c < 0) // ??��???
					break;
				if (x >= 127 || (127 - x < 8)) // 16?????? || ?????��??8???????????????????????
				{
					x = 0;
					y += 16;
					if (63 - y < 16) // ??????????????????
						break;
				}
				for (uint8_t m = 0; m < 2; m++)
				{
					for (uint8_t n = 0; n < 8; n++)
					{
						for (uint8_t i = 0; i < 8; i++)
						{
							OLED_SetPixel(x + n, y + i + m * 8, (F8X16[c][n + m * 8] >> i) & 0x01);
						}
					}
				}
				x += 8;
				len++;
			}
		}
	}
	OLED_RefreshRAM();
}

/***************************************************************
	 Prototype      :	void OLED_DrawBMP(int16_t x0,int16_t y0,int16_t L,int16_t H,const uint8_t BMP[])
	 Parameters     : (x0,y0)????L??H??????????BMP
										0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 ????? ??????????????
	 return				  : none
	 Description    : ??????????????BMP��?,????????????��
***************************************************************/
void OLED_DrawBMP(int16_t x0, int16_t y0, int16_t L, int16_t H, const uint8_t BMP[])
{
	if (x0 >= 0 && x0 < displayWidth && x0 + L <= displayWidth &&
		y0 >= 0 && y0 < displayHeight && y0 + H <= displayHeight)
	{

		uint8_t *p = (uint8_t *)BMP;
		for (int16_t y = y0; y < y0 + H; y += 8)
		{
			for (int16_t x = x0; x < x0 + L; x++)
			{
				for (int16_t i = 0; i < 8; i++)
				{
					//					OLED_SetPixel(x, y+i, ((*((uint8_t *)BMP+(x-x0)+L*((y-y0)/8))) >> i) & 0x01);
					OLED_SetPixel(x, y + i, ((*p) >> i) & 0x01);
				}
				p++;
			}
		}
	}
	OLED_RefreshRAM();
}

/***************************************************************
	 Prototype      :	void OLED_AreaFill(int16_t x0,int16_t y0,int16_t L,int16_t H)
	 Parameters     : ?????????????(x0,y0)????L??H????
										0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 ????? ??????????????
	 return				  : none
	 Description    : ?��???????????
***************************************************************/
void OLED_AreaFill(int16_t x0, int16_t y0, int16_t L, int16_t H, uint8_t fill_data)
{
	if (x0 >= 0 && x0 < displayWidth && x0 + L <= displayWidth &&
		y0 >= 0 && y0 < displayHeight && y0 + H <= displayHeight)
	{

		for (int16_t y = y0; y < y0 + H; y++)
		{
			for (int16_t x = x0; x < x0 + L; x++)
			{
				for (int16_t i = 0; i < 8; i++)
				{
					OLED_SetPixel(x, y + i, (fill_data >> i) & SET_PIXEL);
				}
			}
		}
		OLED_RefreshRAM();
	}
}

/***************************************************************
	 Prototype      :	void OLED_AreaCLR(int16_t x0,int16_t y0,int16_t L,int16_t H)
	 Parameters     : (x0,y0)????L??H????
										0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 ????? ??????????????
	 return				  : none
	 Description    : ?��???????????
***************************************************************/
void OLED_AreaClear(int16_t x0, int16_t y0, int16_t L, int16_t H)
{
	if (x0 >= 0 && x0 < displayWidth && x0 + L <= displayWidth &&
		y0 >= 0 && y0 < displayHeight && y0 + H <= displayHeight)
	{

		for (int16_t y = y0; y < y0 + H; y += 8)
		{
			for (int16_t x = x0; x < x0 + L; x++)
			{
				for (int16_t i = 0; i < 8; i++)
				{
					OLED_SetPixel(x, y + i, RESET_PIXEL);
				}
			}
		}
		OLED_RefreshRAM();
	}
}

/***************************************************************
	 Prototype      :	void OLED_FullyToggle(void)
	 Parameters     : none
	 return				  : none
	 Description    : ???????????????????GDDRAM
***************************************************************/
void OLED_FullyToggle(void)
{
	for (uint16_t m = 0; m < displayHeight / 8; m++)
	{
		for (uint16_t n = 0; n < displayWidth; n++)
		{
			OLED_RAM[m][n] = ~OLED_RAM[m][n];
		}
	}
	OLED_RefreshRAM();
}
/***************************************************************
	 Prototype      :	void OLED_AreaToggle(int16_t x0,int16_t y0,int16_t L,int16_t H)
	 Parameters     : (x0,y0)????L??H????
										0<=x0<=127 0<=y0<=63 0<=L+x0<=127 0<=H+y0<= 63 ????? ??????????????
	 return				  : none
	 Description    : ?��???????????
***************************************************************/
void OLED_AreaToggle(int16_t x0, int16_t y0, int16_t L, int16_t H)
{
	if (x0 >= 0 && x0 < displayWidth && x0 + L <= displayWidth &&
		y0 >= 0 && y0 < displayHeight && y0 + H <= displayHeight)
	{

		for (int16_t y = y0; y < y0 + H; y += 8)
		{
			for (int16_t x = x0; x < x0 + L; x++)
			{
				for (int16_t i = 0; i < 8; i++)
				{
					OLED_SetPixel(x, y + i, !OLED_GetPixel(x, y + i));
				}
			}
		}
		OLED_RefreshRAM();
	}
}

/****************************************************************
	?????????,0->63????
	?????????,??��0-63
	?????????,??��63-0
****************************************************************/
void OLED_VerticalShift(void)
{
	for (uint8_t i = 0; i < displayHeight; i++)
	{
		WriteCmd(0xd3); //???????????0->63????
		WriteCmd(i);	//?????
		HAL_Delay(40);	//??????
	}
}

/****************************************************************
	????????????????????
	??		LEFT	0x27
	??		RIGHT	0x26
****************************************************************/
void OLED_HorizontalShift(uint8_t direction)

{
	WriteCmd(direction); //???��???????
	WriteCmd(0x00);		 //????????????????0x00
	WriteCmd(0x00);		 //??????????
	WriteCmd(0x05);		 //????????????????????????????
	WriteCmd(0x07);		 //???????????
	WriteCmd(0x00);		 //????????????????0x00
	WriteCmd(0xff);		 //????????????????0xff
	WriteCmd(0x2f);		 //????????-0x2f?????��???-0x2e???????????��????
}

/****************************************************************
	??????????????????????
	??		UP		0x29
	??		DOWN	0x2A
****************************************************************/
void OLED_VerticalAndHorizontalShift(uint8_t direction)
{
	WriteCmd(direction); //???��???????
	WriteCmd(0x00);		 //????????????????0x00
	WriteCmd(0x00);		 //??????????
	WriteCmd(0x05);		 //????????????????????????????
	WriteCmd(0x07);		 //???????????
	WriteCmd(0x01);		 //????????????

	WriteCmd(0x2f); //????????-0x2f?????��???-0x2e???????????��????
}

/****************************************************************
	?????????????
	??	ON	0xA7
	??	OFF	0xA6	???????????????????
****************************************************************/
void OLED_DisplayMode(uint8_t mode)
{
	WriteCmd(mode);
}

/****************************************************************
	??????????
	intensity	0-255
	????0x7f
****************************************************************/
void OLED_IntensityControl(uint8_t intensity)
{
	WriteCmd(0x81);
	WriteCmd(intensity);
}
